#include "devinsight.h"


DVI_DEF_GDB_FUNCTION(dviHeapTuple, "解析打印一个HeapTuple的元组内容, 需要tupledesc， null好像有点问题目前。")
const char* dviHeapTuple(HeapTuple tup, TupleDesc desc)
{
    if (tup == NULL || desc == NULL) {
        return "wrong input.";
    }

    StringInfoData res;
    initStringInfo(&res);

    Datum* values = (Datum*)palloc0(sizeof(Datum) * desc->natts);
    bool* nulls = (bool*)palloc0(sizeof(bool) * desc->natts);
    heap_deform_tuple(tup, desc, values, nulls);

    for (int i = 0; i < desc->natts; i++) {
        if (nulls[i]) {
            appendStringInfo(&res, "(null column)\t");
        }
        Form_pg_attribute adesc = &desc->attrs[i];
        HeapTuple typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(adesc->atttypid));
        Form_pg_type typetyp = (Form_pg_type)GETSTRUCT(typtup);
        regproc outfunc = typetyp->typoutput;
        ReleaseSysCache(typtup);

        char* str = DatumGetCString(OidFunctionCall1(outfunc, values[i]));
        appendStringInfo(&res, "%s\n", str);
    }

    pfree(values);
    pfree(nulls);
    return (const char*)res.data;
}


/*
 * 打印 Page
 * null目前好像有点问题
 */
DVI_DEF_GDB_FUNCTION(dviPage, "解析打印一个page的内容, 传入tupledesc的话还会解析出元组， null好像有点问题目前。")
const char* dviPage(Page page, TupleDesc desc)
{
    if (page == NULL) {
        return "wrong input.";
    }

    StringInfoData res;
    initStringInfo(&res);
    
    HeapPageHeader phd = (HeapPageHeader)page;

    /* page header */
    appendStringInfo(&res, "Page Header：\n");
    appendStringInfo(&res, "\tpd_lsn: %X/%X\n", (uint32)(PageGetLSN(page) >> 32), (uint32)PageGetLSN(page));
    appendStringInfo(&res, "\tpd_checksum: 0x%X\n", phd->pd_checksum);
    appendStringInfo(&res, "\tpd_lower: %u, %s\n", phd->pd_lower, PageIsEmpty(page) ? "empty" : "non-empty");
    appendStringInfo(&res, "\tpd_upper: %u, %s\n", phd->pd_upper, PageIsNew(page) ? "new" : "old");
    appendStringInfo(&res, "\tpd_special: %u, size %u\n", phd->pd_special, PageGetSpecialSize(page));
    appendStringInfo(&res, "\tPage size & version: %u, %u\n", (uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
    appendStringInfo(&res, "\tpd_xid_base: %lu, pd_multi_base: %lu\n", phd->pd_xid_base, phd->pd_multi_base);
    appendStringInfo(&res, "\tpd_prune_xid: %lu\n", phd->pd_prune_xid + phd->pd_xid_base);
    appendStringInfo(&res, "\tpd_flags: [%s%s%s%s%s%s]\n\n",
        PageHasFreeLinePointers(page) ? "PD_HAS_FREE_LINES " : "",
        PageIsFull(page) ? "PD_PAGE_FULL " : "",
        PageIsAllVisible(page) ? "PD_ALL_VISIBLE " : "",
        PageIsCompressed(page) ? "PD_COMPRESSED_PAGE " : "",
        PageIsLogical(page) ? "PD_LOGICAL_PAGE " : "",
        PageIsEncrypt(page) ? "PD_ENCRYPT_PAGE " : "");

    /* tuples */
    int nline = PageGetMaxOffsetNumber(page);
    HeapTuple tuple = heaptup_alloc(HEAPTUPLESIZE);
    appendStringInfo(&res, "Tuples：\n");
    for (int i = FirstOffsetNumber; i <= nline; i++) {
        ItemId lp = PageGetItemId(page, i);
        if (!ItemIdIsUsed(lp)) {
            appendStringInfo(&res, "# %d| unused\n", i);
        } else if (ItemIdIsRedirected(lp)) {
            appendStringInfo(&res, "# %d| redirected: length %u, offset %u", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
        } else if (ItemIdIsDead(lp)) {
            appendStringInfo(&res, "# %d| dead: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
        } else if (ItemIdIsNormal(lp)) {
            appendStringInfo(&res, "# %d| normal: length %u, offset %u\n", i, ItemIdGetLength(lp), ItemIdGetOffset(lp));
            if (desc != NULL) {
                tuple->t_data= (HeapTupleHeader)PageGetItem(page, lp);
                tuple->t_len= ItemIdGetLength(lp);
                appendStringInfo(&res, "tuple detail>>>>>>>>>>\n%s<<<<<<<<<<\n", dviHeapTuple(tuple, desc));
            }
        } else {
            appendStringInfo(&res, "# %d|???\n", i);
        }
    }

    /* special data */
    appendStringInfo(&res, "Special Data：\n");
    appendStringInfo(&res, "\ncompress metadata: offset %u, size %d\n", phd->pd_special, PageGetSpecialSize(page));
    
    pfree(tuple);
    return (const char*)res.data;
}


DVI_DEF_GDB_FUNCTION(dviIndexPage, "解析打印一个索引页面page的内容, 没实现还。别用。")
const char* dviIndexPage(Page page, TupleDesc desc=NULL)
{
    if (page == NULL) {
        return "wrong input.";
    }

    StringInfoData res;
    initStringInfo(&res);

    HeapPageHeader phd = (HeapPageHeader)page;

    /* page header */
    appendStringInfo(&res, "Page Header：\n");
    appendStringInfo(&res, "\tpd_lsn: %X/%X\n", (uint32)(PageGetLSN(page) >> 32), (uint32)PageGetLSN(page));
    appendStringInfo(&res, "\tpd_checksum: 0x%X\n", phd->pd_checksum);
    appendStringInfo(&res, "\tpd_lower: %u, %s\n", phd->pd_lower, PageIsEmpty(page) ? "empty" : "non-empty");
    appendStringInfo(&res, "\tpd_upper: %u, %s\n", phd->pd_upper, PageIsNew(page) ? "new" : "old");
    appendStringInfo(&res, "\tpd_special: %u, size %u\n", phd->pd_special, PageGetSpecialSize(page));
    appendStringInfo(&res, "\tPage size & version: %u, %u\n", (uint16)PageGetPageSize(page), (uint16)PageGetPageLayoutVersion(page));
    appendStringInfo(&res, "\tpd_xid_base: %lu, pd_multi_base: %lu\n", phd->pd_xid_base, phd->pd_multi_base);
    appendStringInfo(&res, "\tpd_prune_xid: %lu\n", phd->pd_prune_xid + phd->pd_xid_base);
    appendStringInfo(&res, "\tpd_flags: [%s%s%s%s%s%s]\n\n",
        PageHasFreeLinePointers(page) ? "PD_HAS_FREE_LINES " : "",
        PageIsFull(page) ? "PD_PAGE_FULL " : "",
        PageIsAllVisible(page) ? "PD_ALL_VISIBLE " : "",
        PageIsCompressed(page) ? "PD_COMPRESSED_PAGE " : "",
        PageIsLogical(page) ? "PD_LOGICAL_PAGE " : "",
        PageIsEncrypt(page) ? "PD_ENCRYPT_PAGE " : "");

    /* tuples */
    appendStringInfo(&res, "Index tuples：\n");
    appendStringInfo(&res, "\t\tnot supported.\n\n");

    /* special data */
    appendStringInfo(&res, "Special Data：\n");
    appendStringInfo(&res, "\ncompress metadata: offset %u, size %d\n", phd->pd_special, PageGetSpecialSize(page));

    return (const char*)res.data;
}



